home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / SourceCode / libcs / parsedate.y < prev    next >
Text File  |  1995-06-12  |  35KB  |  1,392 lines

  1. /*
  2.  * Copyright (c) 1990 Carnegie Mellon University
  3.  * All Rights Reserved.
  4.  * 
  5.  * Permission to use, copy, modify and distribute this software and its
  6.  * documentation is hereby granted, provided that both the copyright
  7.  * notice and this permission notice appear in all copies of the
  8.  * software, derivative works or modified versions, and any portions
  9.  * thereof, and that both notices appear in supporting documentation.
  10.  *
  11.  * THE SOFTWARE IS PROVIDED "AS IS" AND CARNEGIE MELLON UNIVERSITY
  12.  * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
  13.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT
  14.  * SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR ANY SPECIAL, DIRECT,
  15.  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
  16.  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
  17.  * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  18.  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  19.  *
  20.  * Users of this software agree to return to Carnegie Mellon any
  21.  * improvements or extensions that they make and grant Carnegie the
  22.  * rights to redistribute these changes.
  23.  *
  24.  * Export of this software is permitted only after complying with the
  25.  * regulations of the U.S. Deptartment of Commerce relating to the
  26.  * Export of Technical Data.
  27.  */
  28. /*
  29.  *  parsedate - parse date specification. Originally pdate.
  30.  *
  31.  **********************************************************************
  32.  * HISTORY
  33.  * $Log:    parsedate.y,v $
  34.  * Revision 1.4  90/12/11  18:03:19  mja
  35.  *     Add copyright/disclaimer for distribution.
  36.  * 
  37.  * 20-Jul-88  Glenn Marcy (gm0w) at Carnegie-Mellon University
  38.  *    Made strings[] array static.
  39.  *
  40.  * 27-Jan-87  Glenn Marcy (gm0w) at Carnegie-Mellon University
  41.  *    Recoded currtm initialization.  The RT compiler does not allow
  42.  *    dereferencing a pointer that is the return value of a routine.
  43.  *
  44.  * 18-Jan-86  Leonard Hamey (lgh) at Carnegie-Mellon University
  45.  *     Fix bug 'sun jan 12 00:00:00 1986' gives year 1992.
  46.  *     Also ingore () and [] in date. Other junk chars become JUNK token.
  47.  *
  48.  * 05-Dec-85  Glenn Marcy (gm0w) at Carnegie-Mellon University
  49.  *    Added second argument to longjmp (don't know why this worked
  50.  *    under 4.1...).  Also added "this upcoming ..." to mean the
  51.  *    next occurance of "..." in the future.  If "..." is today,
  52.  *    then the next occurrence is used.  Added {April,All} Fool[s]
  53.  *    [day].
  54.  *
  55.  * 09-Jul-85  Leonard Hamey (lgh) at Carnegie-Mellon University
  56.  *      Fixed to parse a.m. and p.m. properly. These are special
  57.  *     because the period is a delimiter and creates two tokens.
  58.  *     While we are at it, we handle "a m " and variants.
  59.  *
  60.  * 02-Jan-85  Leonard Hamey (lgh) at Carnegie-Mellon University
  61.  *    Added parsing of "months" and "years" relative constructs.
  62.  *    (e.g. 3 months before today, 2 years from tomorrow.)
  63.  *    Added "ago" constructs (e.g. 3 days ago.).
  64.  *
  65.  * 08-Mar-84  Leonard Hamey (lgh) at Carnegie-Mellon University
  66.  *      Bug fixes, code tidying.
  67.  *
  68.  * 16-Feb-84  Leonard Hamey (lgh) at Carnegie-Mellon University
  69.  *    Modified pre-parser to allow period following an abbreviation.
  70.  *
  71.  * 03-Feb-84  Leonard Hamey (lgh) at Carnegie-Mellon University
  72.  *     Corrected bugs when time preceded date. Introduced code to
  73.  *    increment date if time >= 24:00. Changed NIGHT to mean 5pm
  74.  *    or later. Changed evening to mean 3pm or later. Fixed MIDNIGHT bug.
  75.  *    Allowed parsing 12:00 noon. Handled military time: 0530.
  76.  *
  77.  *    The grammar now compiles with 3 shift/reduce conflicts and 2
  78.  *    reduce/reduce. The reduce/reduce are due to the keyword THE and
  79.  *    dont matter. Two shift/reduce are due to nnnn being a time or a
  80.  *    year. Yacc chooses to take it as a year, which is what we want.
  81.  * 
  82.  * 16-Jan-84  Leonard Hamey (lgh) at Carnegie-Mellon University
  83.  *    Included code to handle words other than months and days.
  84.  *    Revamped grammar to handle all sorts of reasonable date constructs
  85.  *    such as "Wednesday", "This Thursday", "A week from today", etc.
  86.  *    Modified arguments to allow specification of past/future
  87.  *     dates. Defined the existing contents of tm structure to mean
  88.  *    NOW. Returned structure may include values of -1 for unspecified
  89.  *    fields. Also, if date is properly specified then weekday and yearday
  90.  *    is always computed. (Omitted year is internally represented by NOYEAR)
  91.  *    Increased character limit to 80 characters. Introduced
  92.  *    parsedate () with more arguments than pdate ().
  93.  *
  94.  * 01-Jan-83  Mike Accetta (mja) at Carnegie-Mellon University
  95.  *    Fixed bug in month number recording which neglected to subtract one
  96.  *    from month numbers specified in the form mm/dd/yy form before storing
  97.  *    in the tm_mon field.
  98.  *
  99.  * 21-Feb-80  Mike Accetta (mja) at Carnegie-Mellon University
  100.  *    Fixed bug in grammar which failed to parse dates followed by
  101.  *    times when no year was specified in the date;  changed
  102.  *    date string limit from 25 to 50 characters.
  103.  *
  104.  * 03-Jan-80  Mike Accetta (mja) at Carnegie-Mellon University
  105.  *    Created.
  106.  *
  107.  **********************************************************************
  108.  */
  109.  
  110. %start date_time
  111. %token NUMBER NUMBER4 WEEKDAY MONTH HOUR SHOUR
  112. %token TODAY NOW TONIGHT NEXT THIS DAY WEEK FORTNIGHT UPCOMING
  113. %token EVERY FROM BEFORE THE A AT ON LAST AFTER IN OF AGO WORD_MONTH
  114. %token NOON AMPM TIMEKEY
  115. %token AND NWORD NTHWORD ST ND RD TH
  116. %token CHRISTMAS NEW YEAR ALL FOOLS
  117. %token JUNK
  118.  
  119. /* Resolve shift/reduce conflicts on THE */
  120.  
  121. %left THE
  122.  
  123. %{
  124. #include <sys/types.h>
  125. #include <setjmp.h>
  126. #include <sys/time.h>
  127. #include <c.h>
  128.  
  129. # define MAXPEEP 3
  130. # define NDTMS 10
  131. # define NOYEAR -1901
  132. /* PTM: indicates ptm */
  133. # define PTM -1
  134. /* CURRTM: indicates current time */
  135. # define CURRTM -2
  136. /* CURRDATE: indicates current date (time set to -1). */
  137. # define CURRDATE -3
  138. /* PAST, FUTURE: logical values for past and future */
  139. # define PAST 1
  140. # define FUTURE 0
  141.  
  142. static int junk_err;            /* junk token give error? */
  143. static int ppf;                /* past, or future */
  144. static int nottoday;            /* future doesn't include today */
  145. static struct tm scurrtm;
  146. static struct tm *currtm = &scurrtm;    /* current time */
  147. static char *strp;            /* current position in date string */
  148. static int delim;            /* previous field delimiter */
  149. static jmp_buf errbuf;            /* jump location for parse errors */
  150. extern char *nxtarg();
  151. struct stable { char *text;  int token;  int lexval; };
  152. static int shour, tmkey;
  153. static struct tm ptm;
  154. static int pcount;
  155. static int result;
  156.  
  157. /* Date structure for constraining dates */
  158. struct dtm 
  159. { int repn;                      /* Current representation */
  160.   int days;
  161.   struct tm tm;
  162.   int count;
  163. };
  164.  
  165. static struct dtm dtm[NDTMS];
  166. static int dtmused[NDTMS];
  167.  
  168. /* Representations */
  169. # define RDAYS 0
  170. # define RTM 1
  171.  
  172. /*
  173.  *  Month and week day names (upper case only) and other words.
  174.  */
  175. static
  176. struct stable strings[] =
  177. {
  178.   { "JAN*UARY", MONTH, 0 },        /* months (0-11) */
  179.   { "FEB*RUARY", MONTH, 1 },
  180.   { "MAR*CH", MONTH, 2 },
  181.   { "APR*IL", MONTH, 3 },
  182.   { "MAY", MONTH, 4 },
  183.   { "JUN*E", MONTH, 5 },
  184.   { "JUL*Y", MONTH, 6 },
  185.   { "AUG*UST", MONTH, 7 },
  186.   { "SEP*TEMBER", MONTH, 8 },
  187.   { "OCT*OBER", MONTH, 9 },
  188.   { "NOV*EMBER", MONTH, 10 },
  189.   { "DEC*EMBER", MONTH, 11 },
  190.   { "SUN*DAY", WEEKDAY, 0 },    /* days of the week (0-6) */
  191.   { "MON*DAY", WEEKDAY, 1 },
  192.   { "TUE*SDAY", WEEKDAY, 2 },
  193.   { "WED*NESDAY", WEEKDAY, 3 },
  194.   { "THU*RSDAY", WEEKDAY, 4 },
  195.   { "FRI*DAY", WEEKDAY, 5 },
  196.   { "SAT*URDAY", WEEKDAY, 6 },
  197.   { "YESTERDAY", TODAY, -1 },    /* relative to today */
  198.   { "TODAY", TODAY, 0 },
  199.   { "TONIGHT", TONIGHT, 0 },
  200.   { "NOW", NOW, 0 },
  201.   { "AGO", AGO, 0 },
  202.   { "TOMORROW", TODAY, 1 },
  203.   { "NEXT", NEXT, 0 },        /* keywords */
  204.   { "THIS", THIS, 0 },
  205.   { "UPCOMING", UPCOMING, 0 },
  206.   { "DAY*S", DAY, 0 },
  207.   { "WEEK*S", WEEK, 0 },
  208.   { "MONTH*S", WORD_MONTH, 0 },
  209.   { "YEAR*S", YEAR, 0 },
  210.   { "ALL", ALL, 0 },
  211.   { "FOOL*S", FOOLS, 0 },
  212.   { "FORTNIGHT", FORTNIGHT, 0 },    /* two weeks (Australian) */
  213. /*  { "EVERY", EVERY, 0 }, */
  214.   { "FROM", FROM, 0 },
  215.   { "AFTER", AFTER, 0 },
  216.   { "BEFORE", BEFORE, 0 },
  217.   { "LAST", LAST, 0 },
  218.   { "THE", THE, 0 },
  219.   { "A", A, 0 },
  220.   { "AT", AT, 0 },
  221.   { "ON", ON, 0 },
  222.   { "IN", IN, 0 },
  223.   { "OF", OF, 0 },
  224. /*  { "AND", AND, 0 }, */
  225.   { "MORNING", TIMEKEY, 0 },    /* time keywords. Morning is 0:00 - 11:59 */
  226.   { "AFTERNOON", TIMEKEY, 12 }, /* Afternoon is 12:00 - 23:59 */
  227.   { "EVENING", TIMEKEY, 15 },    /* Evening is 15:00 - 02:59 */
  228.   { "NIGHT", TIMEKEY, 17 },    /* Night is 17:00 - 04:59 */
  229.   { "NOON", NOON, 12 },        /* time specifications */
  230.   { "MIDNIGHT", NOON, 24 },
  231.   { "ONE", NWORD, 1 },        /* numbers up to 19 */
  232.   { "TWO", NWORD, 2 },
  233.   { "THREE", NWORD, 3 },
  234.   { "FOUR", NWORD, 4 },
  235.   { "FIVE", NWORD, 5 },
  236.   { "SIX", NWORD, 6 },
  237.   { "SEVEN", NWORD, 7 },
  238.   { "EIGHT", NWORD, 8 },
  239.   { "NINE", NWORD, 9 },
  240.   { "TEN", NWORD, 10 },
  241.   { "ELEVEN", NWORD, 11 },
  242.   { "TWELVE", NWORD, 12 },
  243.   { "THIRTEEN", NWORD, 13 },
  244.   { "FOURTEEN", NWORD, 14 },
  245.   { "FIFTEEN", NWORD, 15 },
  246.   { "SIXTEEN", NWORD, 16 },
  247.   { "SEVENTEEN", NWORD, 17 },
  248.   { "EIGHTEEN", NWORD, 18 },
  249.   { "NINETEEN", NWORD, 19 },
  250.   { "FIRST", NTHWORD, 1 },        /* number up to 19th */
  251.   { "SECOND", NTHWORD, 2 },
  252.   { "THIRD", NTHWORD, 3 },
  253.   { "FOURTH", NTHWORD, 4 },
  254.   { "FIFTH", NTHWORD, 5 },
  255.   { "SIXTH", NTHWORD, 6 },
  256.   { "SEVENTH", NTHWORD, 7 },
  257.   { "EIGHT", NTHWORD, 8 },
  258.   { "NINTH", NTHWORD, 9 },
  259.   { "TENTH", NTHWORD, 10 },
  260.   { "ELEVENTH", NTHWORD, 11 },
  261.   { "TWELFTH", NTHWORD, 12 },
  262.   { "THIRTEENTH", NTHWORD, 13 },
  263.   { "FOURTEENTH", NTHWORD, 14 },
  264.   { "FIFTEENTH", NTHWORD, 15 },
  265.   { "SIXTEENTH", NTHWORD, 16 },
  266.   { "SEVENTEENTH", NTHWORD, 17 },
  267.   { "EIGHTEENTH", NTHWORD, 18 },
  268.   { "NINETEENTH", NTHWORD, 19 },
  269.   { "ST", ST, 0 },        /* for 1st */
  270.   { "ND", ND, 0 },        /* 2nd */
  271.   { "RD", RD, 0 },        /* 3rd */
  272.   { "TH", TH, 0 },        /* nth */
  273.   { "AM", AMPM, 0 },        /* time qualifiers */
  274.   { "A.M.", AMPM, 0 },
  275.   { "PM", AMPM, 12 },
  276.   { "P.M.", AMPM, 12 },
  277.   { "CHRISTMAS", CHRISTMAS, 1225 },    /* special dates */
  278.   { "NEW", NEW, 101 },
  279.   { 0, 0, 0 }
  280. };
  281.  
  282. %}
  283. %%
  284.  
  285. /* Grammar for legal dates. The date is returned in the dtm structure indexed
  286.  * by result. The time is returned in ptm (and, possibly, shour).
  287.  */
  288.  
  289. date_time :    sdate_time
  290.             { result = $$;
  291.               check ($$); }
  292.     ;
  293.  
  294. sdate_time :    stime
  295.             { $$ = new_dtm (CURRDATE); }
  296.     |    full_date time
  297.     |    full_date time year
  298.         /* 
  299.          * This is a hack. To handle the strange format
  300.          * day month day-in-month time year
  301.          * It parses the date as though it were a full
  302.          * specification and then substitutes the year.
  303.          */
  304.             { setrep (&(dtm[$$]), RTM);
  305.               dtm[$$].tm.tm_year = ptm.tm_year;
  306.               dtm[$$].tm.tm_wday = -1;
  307.               dtm[$$].tm.tm_yday = -1; }
  308.     |    time ON full_date
  309.             { $$ = $3; }
  310.     |    time full_date
  311.             { $$ = $2; }
  312.     |    full_date
  313.     |    time
  314.             { $$ = new_dtm (CURRDATE); }
  315.     |    stime today
  316.             { $$ = new_dtm (CURRDATE);
  317.               incr ($$, $2); }
  318.     |    NOW
  319.             { $$ = new_dtm (CURRDATE);
  320.               ptm.tm_hour = currtm->tm_hour;
  321.               ptm.tm_min = currtm->tm_min;
  322.               ptm.tm_sec = currtm->tm_sec; }
  323.     ;
  324.  
  325. full_date :    rec_date
  326.     |    timekey _of rec_date
  327.             { $$ = $3; }
  328.     |    THE timekey _of rec_date
  329.             { $$ = $4; }
  330.     |    rec_date timekey
  331.     ;
  332.  
  333. rec_date :    date
  334.             { check ($$); }
  335.     ;
  336.  
  337. date    :    partial_date
  338.             { $$ = new_dtm (CURRDATE);
  339.               constrain (PTM, $$, ppf, 1); }
  340.     |    THIS UPCOMING relative_partial_date
  341.             { $$ = new_dtm (CURRDATE);
  342.               nottoday = 1;
  343.               constrain (PTM, $$, FUTURE, 1); }
  344.     |    THIS relative_partial_date
  345.             { $$ = new_dtm (CURRDATE);
  346.               constrain (PTM, $$, FUTURE, 1); }
  347.     |    NEXT relative_partial_date
  348.             { $$ = new_dtm (CURRDATE);
  349.               constrain (PTM, $$, FUTURE, 2); }
  350.     |    LAST relative_partial_date
  351.             { $$ = new_dtm (CURRDATE);
  352.               incr ($$, -1);
  353.               constrain (PTM, $$, PAST, 1); }
  354.     |    weekday WEEK
  355.             { $$ = new_dtm (CURRDATE);
  356.               constrain (PTM, $$, FUTURE, 1);
  357.               incr ($$, 7); }
  358.     |    weekday FORTNIGHT
  359.             { $$ = new_dtm (CURRDATE);
  360.               constrain (PTM, $$, FUTURE, 1);
  361.               incr ($$, 14); }
  362.     |    adjusted_date
  363.     |    today
  364.             { $$ = new_dtm (CURRDATE);
  365.               incr ($$, $1); }
  366.     |    TODAY WEEK
  367.             { $$ = new_dtm (CURRDATE);
  368.               incr ($$, $1 + 7); }
  369.     |    TODAY FORTNIGHT
  370.             { $$ = new_dtm (CURRDATE);
  371.               incr ($$, $1 + 14); }
  372.     |    stddate
  373.             { $$ = new_dtm (PTM); }
  374.     |    weekday stddate
  375.             { $$ = new_dtm (PTM); }
  376.     ;
  377.  
  378. stddate    :    yearday year
  379.     |    yearday '-' year
  380.     |    yearday '/' year
  381.     ;
  382.  
  383. /* The following rule must precede other uses of relative_yearday and
  384.  * nonrelative_yearday so that the preferred reduction is to yearday.
  385.  * This, in turn, causes a 4-digit number to be preferred as a year
  386.  * rather than military time. */
  387.  
  388. yearday :    relative_yearday
  389.     |    nonrelative_yearday
  390.     ;
  391.  
  392. relative_partial_before : relative_partial_dtm
  393.     |    LAST relative_partial_date
  394.             { $$ = new_dtm (PTM); }
  395.     |    nth LAST weekday
  396.             { $$ = new_dtm (PTM);
  397.               dtm[$$].count = $1; }
  398.     ;
  399.  
  400. relative_partial_dtm :    relative_partial_date
  401.             { $$ = new_dtm (PTM); }
  402.     |    nth weekday
  403.             { $$ = new_dtm (PTM);
  404.               dtm[$$].count = $1; }
  405.     ;
  406.  
  407. /* 
  408.  * partial_date: Returns ptm.
  409.  */
  410.  
  411. partial_date : relative_partial_date
  412.     |    nonrelative_yearday
  413.     ;
  414.  
  415. relative_partial_date :    relative_yearday
  416.     |    weekday
  417.     |    nth
  418.             { ptm.tm_mday = $1; }
  419.     |    weekday nth
  420.             { ptm.tm_mday = $2; }
  421.     |    weekday relative_yearday
  422.     ;
  423.  
  424. weekday    :    WEEKDAY
  425.             { ptm.tm_wday = $1; }
  426.     ;
  427.  
  428. holiday :    sholiday
  429.             { ptm.tm_mon = $1/100-1; ptm.tm_mday = $1%100; }
  430.     ;
  431.  
  432. sholiday :    CHRISTMAS
  433.     |    CHRISTMAS DAY
  434.     |    NEW YEAR
  435.     |    ALL FOOLS
  436.             { $$ = 401; }
  437.     |    ALL FOOLS DAY
  438.             { $$ = 401; }
  439.     |    MONTH FOOLS
  440.             { if ($1 != 3)  yyerror ();
  441.               $$ = 401; }
  442.     |    MONTH FOOLS DAY
  443.             { if ($1 != 3)  yyerror ();
  444.               $$ = 401; }
  445.     ;
  446.  
  447. /* 
  448.  * forward_rec_date: Returns index to dtm.
  449.  */
  450.  
  451. forward_rec_date : AFTER rec_date
  452.             { $$ = $2; }
  453.     |    FROM rec_date
  454.             { $$ = $2; }
  455.     ;
  456.  
  457. /* 
  458.  * adjusted_date: Returns index to dtm.
  459.  */
  460.  
  461. adjusted_date :    days forward_rec_date
  462.             { $$ = $2;
  463.               incr ($$, $1); }
  464.     |    days BEFORE rec_date
  465.             { $$ = $3;
  466.               incr ($$, -$1); }
  467.     |    days AGO
  468.             { $$ = new_dtm (CURRTM);
  469.               incr ($$, -$1); }
  470.     |    months forward_rec_date
  471.             { $$ = $2;
  472.               incrmonth ($$, $1); }
  473.     |    months BEFORE rec_date
  474.             { $$ = $3;
  475.               incrmonth ($$, -$1); }
  476.     |    months AGO
  477.             { $$ = new_dtm (CURRDATE);
  478.               incrmonth ($$, -$1); }
  479.     |    years forward_rec_date
  480.             { $$ = $2;
  481.               incryear ($$, $1); }
  482.     |    years BEFORE rec_date
  483.             { $$ = $3;
  484.               incryear ($$, -$1); }
  485.     |    years AGO
  486.             { $$ = new_dtm (CURRDATE);
  487.               incryear ($$, -$1); }
  488.     |    rel_date
  489.     |    THE rel_date
  490.             { $$ = $2; }
  491.     ;
  492.  
  493. /* 
  494.  * rel_date: Parse date relative to another date. Returns index to
  495.  * dtm structure containing the resolved date.
  496.  */
  497.  
  498. rel_date :    relative_partial_dtm AFTER rec_date
  499.             { $$ = $3;
  500.               incr ($$, 1);
  501.               constrain ($1, $$, FUTURE, dtm[$1].count); }
  502.     |    relative_partial_before BEFORE rec_date
  503.             { $$ = $3;
  504.               incr ($$, -1);
  505.               constrain ($1, $$, PAST, dtm[$1].count); }
  506.     ;
  507.  
  508. /* 
  509.  * today: Parse an indication of the current day.
  510.  * Side effect: sets tmkey if time of day indication is also made.
  511.  */
  512.  
  513. today    :    TODAY
  514.     |    TONIGHT
  515.             { tmkey = 17; }
  516.     |    THIS timekey
  517.     |    TODAY timekey
  518.             { if ($1 == 0) yyerror (); }
  519.     ;
  520.  
  521. /* 
  522.  * months: Parse a number of months. Returns integer the number of months.
  523.  */
  524.  
  525. months    :    number WORD_MONTH
  526.             { $$ = $1; }
  527.     |    A WORD_MONTH
  528.             { $$ = 1; }
  529.     ;
  530.  
  531. /* 
  532.  * years: Parse a number of years. Returns integer the number of years.
  533.  */
  534.  
  535. years    :    number YEAR
  536.             { $$ = $1; }
  537.     |    A YEAR
  538.             { $$ = 1; }
  539.     ;
  540.  
  541. /* 
  542.  * days: Parse a number of days. Returns integer number of days.
  543.  */
  544.  
  545. days    :    number DAY
  546.     |    nth_day
  547.     |    the_nth_day
  548.     |    THE DAY
  549.             { $$ = 1; }
  550.     |    A DAY
  551.             { $$ = 1; }
  552.     |    DAY
  553.             { $$ = 1; }
  554.     |    number WEEK
  555.             { $$ = $1 * 7; }
  556.     |    A WEEK
  557.             { $$ = 7; }
  558.     |    WEEK
  559.             { $$ = 7; }
  560.     |    A FORTNIGHT
  561.             { $$ = 14; }
  562.     |    FORTNIGHT
  563.             { $$ = 14; }
  564.     ;
  565.  
  566. /* 
  567.  * nonrelative_yearday: Parse day of the year.
  568.  * Returns parsed specification in ptm.
  569.  */
  570.  
  571. nonrelative_yearday : month_name THE nth
  572.             { ptm.tm_mday = $3; }
  573.     |    stdmonthday _of month_name
  574.     |    THE stdmonthday _of month_name
  575.     |    the_nth_day OF month_name
  576.             { ptm.tm_mday = $1; }
  577.     |    nth_day OF month_name
  578.             { ptm.tm_mday = $1; }
  579.     ;
  580.  
  581. /* 
  582.  * relative_yearday: Parse day within year. Return ptm.
  583.  */
  584.  
  585. relative_yearday : month '/' monthday
  586.     |    month_name monthday
  587.     |    month_name '-' monthday
  588.     |    stdmonthday '-' month_name
  589.     |    stdmonthday '-' month
  590.     |    holiday
  591.     ;
  592.  
  593. /* 
  594.  * time: Returns ptm containing the parsed time spec.
  595.  */
  596.  
  597. time    :    ttime
  598.     |    AT ttime
  599.     |    AT stime
  600.     ;
  601.  
  602. stime    :    simple_time
  603.     |    simple_time IN THE timekey
  604.     ;
  605.  
  606. simple_time :    number
  607.             { if ($1 < 1 || $1 > 12) yyerror ();
  608.               shour = $1; }
  609.     ;
  610.  
  611. timekey :    TIMEKEY
  612.             { tmkey = $1; }
  613.     ;
  614.  
  615. /* 12am is midnight. i.e. 0:00.  12pm is noon. */
  616. ttime    :    hm_time
  617.     |    hm_time IN THE timekey
  618.     |    hour ':' min ':' sec
  619.     |    whour AMPM
  620.             { tmkey = $2; }
  621.     |    hm_time AMPM
  622.             { tmkey = $2; }
  623.     |    NOON
  624.             { ptm.tm_hour = $1; }
  625.     |    whour NOON
  626.             { if ($1 != 12) yyerror ();
  627.               shour = -1;
  628.               ptm.tm_hour = $2; }
  629.     |    hm_time NOON
  630.             { if (shour != 12 || ptm.tm_min != 0) yyerror ();
  631.               shour = -1;
  632.               ptm.tm_hour = $2; }
  633.     |    NUMBER4
  634.             { ptm.tm_hour = $1 / 100;
  635.               ptm.tm_min = $1 % 100;
  636.               if (ptm.tm_min > 59) yyerror ();
  637.               if ($1 > 2400) yyerror (); }
  638.     ;
  639.  
  640. hm_time    :    hour ':' min
  641.     |    hour '.' min
  642.     ;
  643.  
  644. month_name :    MONTH
  645.             { ptm.tm_mon = $1; }
  646.     ;
  647.  
  648. /* 
  649.  * year: Parse year specification.
  650.  * Returns ptm.tm_year set to the year specified.
  651.  */
  652.  
  653. year    :    NUMBER
  654.             { ptm.tm_year = $1 + (($1>=100)?-1900:0); }
  655.     |    NUMBER4
  656.             { ptm.tm_year = $1 - 1900; }
  657.     |    THIS YEAR
  658.             { ptm.tm_year = currtm->tm_year; }
  659.     |    LAST YEAR
  660.             { ptm.tm_year = currtm->tm_year - 1; }
  661.     |    NEXT YEAR
  662.             { ptm.tm_year = currtm->tm_year + 1; }
  663.     ;
  664.  
  665. /* 
  666.  * month: Parse numeric month. Store (0 origin) in ptm.tm_mon.
  667.  */
  668.  
  669. month    :    NUMBER
  670.             { ptm.tm_mon = $1 - 1; }
  671.     ;
  672.  
  673. monthday :    NWORD
  674.             { ptm.tm_mday = $1; }
  675.     |    stdmonthday
  676.     ;
  677.  
  678. stdmonthday :    NUMBER
  679.             { ptm.tm_mday = $1; }
  680.     |    nth
  681.             { ptm.tm_mday = $1; }
  682.     ;
  683.  
  684. hour    :    HOUR
  685.             { ptm.tm_hour = $1; }
  686.     |    SHOUR
  687.             { shour = $1; }
  688.     ;
  689.  
  690. whour    :    hour
  691.     |    NWORD
  692.             { shour = $1; }
  693.     ;
  694.  
  695. min    :    NUMBER
  696.             { ptm.tm_min = $1; }
  697.     |    HOUR
  698.             { ptm.tm_min = $1; }
  699.     |    SHOUR
  700.             { ptm.tm_min = $1; }
  701.     ;
  702.  
  703. sec    :    NUMBER
  704.             { ptm.tm_sec = $1; }
  705.     ;
  706.  
  707. number    :    NWORD
  708.     |    NUMBER
  709.     ;
  710.  
  711. /* 
  712.  * nth: Parse 1st 2nd etc or first second etc.
  713.  * Return integer value of the number.
  714.  */
  715.  
  716. nth    :    NTHWORD
  717.     |    NUMBER ST
  718.             { if ($1 % 10 != 1 || $1 % 100 == 11) yyerror ();
  719.               $$ = $1; }
  720.     |    NUMBER ND
  721.             { if ($1 % 10 != 2 || $1 % 100 == 12) yyerror ();
  722.               $$ = $1; }
  723.     |    NUMBER RD
  724.             { if ($1 % 10 != 3 || $1 % 100 == 13) yyerror ();
  725.               $$ = $1; }
  726.     |    NUMBER TH
  727.             { if (($1 + 9) % 10 <= 2 && ($1 % 100) / 10 != 1)
  728.                 yyerror ();
  729.               $$ = $1; }
  730.     ;
  731.  
  732. _of    :    OF
  733.     |
  734.     ;
  735.  
  736. the_nth_day :    THE nth_day
  737.             { $$ = $2; }
  738.     ;
  739.  
  740. nth_day    :    nth DAY
  741.     ;
  742. %%
  743.  
  744. parsedate (str, tmp, settm, select, err)
  745. char *str;
  746. struct tm *tmp;
  747. int settm, select, err;
  748. {   long time (), curtim;
  749.     struct tm *localtime ();
  750.     char tstr[81];
  751.     int i;
  752.     register struct tm *rtm;
  753.  
  754.     junk_err = err;
  755.     if (settm)
  756.     { curtim = time (0);
  757.       rtm = localtime (&curtim);
  758.       *currtm = *rtm;
  759.     }
  760.     else
  761.     { *currtm = *tmp;
  762.     }
  763.  
  764.     /*  initialize lexical analyzer  */
  765.     for (i = 0; i < NDTMS; i++)
  766.       dtmused[i] = 0;
  767.     strcpyn(strp = tstr, str, 80);
  768.     tstr[80] = 0;
  769.     ptm.tm_year = NOYEAR;
  770.     ptm.tm_mon = -1;
  771.     ptm.tm_mday = -1;
  772.     ptm.tm_wday = -1;
  773.     ptm.tm_yday = -1;
  774.     ptm.tm_hour = -1;
  775.     ptm.tm_min = -1;
  776.     ptm.tm_sec = -1;
  777.     pcount = 0;
  778.     currtm->tm_yday = currtm->tm_wday = -1;
  779.     shour = -1;
  780.     tmkey = -1;
  781.     delim = 0;
  782.     ppf = select;
  783.     nottoday = 0;
  784.  
  785.     if (setjmp(errbuf) == 0)
  786.     {
  787.     yyparse();
  788.     rtm = &(dtm[result].tm);
  789.     setrep (&dtm[result], RTM);
  790.     rtm->tm_hour = ptm.tm_hour;
  791.     rtm->tm_min = ptm.tm_min;
  792.     rtm->tm_sec = ptm.tm_sec;
  793.     time_def (rtm, currtm);
  794.     /* If time is 24:00 or later, advance date to next day. */
  795.     if (rtm->tm_hour >= 24)
  796.     { incr (result, rtm->tm_hour / 24);
  797.       setrep (&dtm[result], RTM);
  798.       rtm->tm_hour = rtm->tm_hour % 24;
  799.     }
  800.     if (dtm[result].repn == RTM && dtm[result].tm.tm_yday == -1)
  801.       setrep (&dtm[result], RDAYS);
  802.     setrep (&dtm[result], RTM);
  803.     *tmp = dtm[result].tm;
  804.     return(0);            /* return here on successful parse */
  805.     }
  806.     else
  807.     return(CERROR);            /* return here on error */
  808.  
  809. }
  810.  
  811. /*
  812.  *  yyerror - error routine (called by yyparse)
  813.  *
  814.  *     Performs a jump to the error return location established
  815.  *  by pdate().
  816.  */
  817.  
  818. static yyerror()
  819. {
  820.  
  821.     longjmp(errbuf, 1);
  822.  
  823. }
  824.  
  825. static int prelval;
  826. static struct { int token; int val; } peep[MAXPEEP];
  827. static int peepb = 0, peepe = 0;
  828. static int lead0;
  829.  
  830. /*  yylex - return next token in date string.
  831.  * 
  832.  *  Obtains the tokens from nextlex() and returns them. Checks for
  833.  *  NUMBER tokens which are really HOURs. This is done by peeping ahead.
  834.  *  It gives more lookahead than yacc can support.
  835.  */
  836.  
  837. static int yylex ()
  838. { register int ctoken, htoken;
  839.   ctoken = nextlex();
  840.   htoken = lead0 ? HOUR : SHOUR;
  841.   if (ctoken == NUMBER && yylval <= 24)
  842.   { /* Possible hour - check it out */
  843.     peeper (1);                  /* Allow 1 token peeping */
  844.     if (peep[0].token == AMPM || peep[0].token == NOON)
  845.       return (htoken);                  /* NN AM or NN PM or 12 NOON */
  846.     if (peep[0].token == ':' || peep[0].token == '.')
  847.     { peeper (2);
  848.       if (peep[1].token == NUMBER)
  849.     return (htoken);             /* NN:NN or NN.NN */
  850.     }
  851.   }
  852.   return (ctoken);
  853. }
  854.  
  855. static int nextlex ()
  856. { register int token;
  857.   if (peepb < peepe)
  858.   { token = peep[peepb].token;
  859.     yylval = peep[peepb].val;
  860.     peepb++;
  861.   }
  862.   else
  863.   { token = prelex ();
  864.     yylval = prelval;
  865.   }
  866.   return (token);
  867. }
  868.  
  869. static peeper (n)
  870. int n;
  871. { register int i;
  872.   if (peepb != 0)
  873.   { for (i = peepb; i < peepe; i++)
  874.       peep[i-peepb] = peep[i];
  875.     peepe -= peepb;
  876.   }
  877.   peepb = 0;
  878.   while (peepe < n)
  879.   { peep[peepe].token = prelex ();
  880.     peep[peepe].val = prelval;
  881.     peepe++;
  882.   }
  883. }
  884.  
  885. /*
  886.  *  prelex - return next token in date string
  887.  *
  888.  *     Uses nxtarg() to parse the next field of the date string.
  889.  *  If a non-space, tab, comma or newline delimiter terminated the
  890.  *  previous field it is returned before the next field is parsed.
  891.  *
  892.  *     Returns either one of the delimiter characters " -:/.", the token
  893.  *  from a match on the table (with the associated value in yylval), or
  894.  *  NUMBER, or JUNK.
  895.  *  JUNK is any unrecognized token (depending on the call arguments to
  896.  *  parsedate - an unrecognized token may instead be returned as -1 simply
  897.  *  terminating the parse.)
  898.  *  NUMBER is a numeric string.  NUMBER4 is a 4-digit number.
  899.  *  If the numeric string commences with 0, then lead0 is true.
  900.  *  '@' sign is treated as the token AT.
  901.  */
  902.  
  903. static int wasabb;            /* tabfind indicates abbrev. */
  904.  
  905. static int prelex()
  906. {
  907.  
  908.   register int ret;            /* temp for previous delimiter */
  909.   register char *fp;            /* current field */
  910.   register int find, ndig;
  911.   extern char _argbreak;        /* current delimiter */
  912.  
  913.   while (1)
  914.   { if (ret=delim)
  915.     {
  916.     delim = 0;
  917.         if (ret == '@')
  918.         return (AT);
  919.         /* 
  920.          * Ignore all but the good characters
  921.          */
  922.     if (ret != ':' && ret != '/' && ret != '-' && ret != '.')
  923.         ret = 0;
  924.         if (ret != 0)
  925.       return (ret);
  926.     }
  927.     if (*strp == 0) return (0);
  928.     while (*strp == ' ' || *strp == '\t' || *strp == '\n')
  929.       strp++;
  930.  
  931.     if (*strp >= '0' && *strp <= '9')
  932.     { prelval = 0;
  933.       ndig = 0;
  934.       lead0 = *strp == '0';
  935.       while (*strp >= '0' && *strp <= '9')
  936.       { prelval = prelval * 10 + *strp - '0';
  937.     strp++;
  938.     ndig++;
  939.       }
  940.       if (ndig == 4)
  941.         return (NUMBER4);
  942.       return (NUMBER);
  943.     }
  944.     fp = nxtarg (&strp, " \t,-:/.@()[]\n");
  945.     delim = _argbreak;
  946.     if (*fp == 0 && delim == 0) return (0);  /* End of input string */
  947.     if (*fp != 0)                  /* skip null tokens */
  948.     { foldup(fp, fp);
  949.       /* Because of the embedded period, a.m. and p.m. do not work.
  950.        * Solution is to recognize them explicitly.
  951.        */
  952.       if (fp[1] == '\0' && (delim == '.' || delim == ' ') &&
  953.         (*fp == 'A' || *fp == 'P') && (*strp == 'M' || *strp == 'm') && 
  954.         (strp[1] == '.' || strp[1] == ' ' || strp[1] == '\t' ||
  955.           strp[1] == '\0'))
  956.       { /* It is a.m. or p.m. */
  957.         prelval = *fp == 'A' ? 0 : 12;
  958.         strp += 2;    /* Skip the m. */
  959.         delim = 0;
  960.         return (AMPM);
  961.       }
  962.       if ((find = tabfind(fp, strings)) >= 0)
  963.       {
  964.     if (wasabb && delim == '.')    /* If tabfind found abbrev */
  965.       delim = 0;            /* Discard period after abbrev. */
  966.         prelval = strings[find].lexval;
  967.         return(strings[find].token);
  968.       }
  969.       if (junk_err)
  970.         return (JUNK);
  971.       else
  972.     return (0);
  973.     }
  974.   }
  975. }
  976.  
  977. /* subroutines useful for manipulating dates and used by
  978.  * parsedate.y.
  979.  * 
  980.  * Copyright (c) Leonard G C Hamey, December 1983.
  981.  * 
  982.  * date_days (tm): converts the date portion of tm structure to days since
  983.  *   1 jan 1900 + 693960. Also can be used to obtain weekday (sunday == 0)
  984.  *   by taking modulo 7.
  985.  *
  986.  * days_date: converts days since 1/1/1900 + 693960 to date in tm structre.
  987.  * 
  988.  * tabfind: searches a table of keywords for date parsing.
  989.  * 
  990.  * constrain: fills in default fields under control of past/future
  991.  *     parameter.
  992.  */
  993.  
  994. static int date_days (tm)
  995. struct tm *tm;
  996. {   /* Number of days since 1/1/1900 + 693960 */
  997.     int dd = tm->tm_mday, mm = tm->tm_mon + 1, yyyy = tm->tm_year + 1900;
  998.     int f;
  999.  
  1000.     if (mm >= 3)
  1001.     {   f=365*yyyy+dd+31*mm-31-(4*mm+23)/10+yyyy/4-(75+(yyyy/100)*75)/100;
  1002.     }
  1003.     else
  1004.     {   f=365*yyyy+dd+31*mm-31+(yyyy-1)/4-(75+((yyyy-1)/100)*75)/100;
  1005.     }
  1006.  
  1007.     return (f-1);
  1008. }
  1009.  
  1010. static int monthend[] =
  1011. { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 };
  1012.  
  1013. static days_date (d, tm)
  1014. int d;
  1015. struct tm *tm;
  1016. {   /* Number of days since 1/1/1900 -> mm/dd/yyyy and weekday */
  1017.     int t = d - 693960, leap;
  1018.     int mm, yyyy;
  1019.     tm->tm_year = 0;                  /* 1900 */
  1020.     tm->tm_mon = 0;                  /* Jan */
  1021.     tm->tm_mday = 1;
  1022.     while (t < 0)                 /* handle dates before 1900 */
  1023.     {   tm->tm_year += t / 366 - 1;
  1024.         t = d - date_days (tm);
  1025.     }
  1026.     while (t >= 366)
  1027.     {   tm->tm_year += t / 366;
  1028.         t = d - date_days (tm);
  1029.     }
  1030.     yyyy = tm->tm_year + 1900;
  1031.     leap = yearsize (yyyy) == 366;
  1032.     if (! leap && t == 365)
  1033.     { tm->tm_year++;
  1034.       t = 0;
  1035.     }
  1036.     tm->tm_yday = t;                 /* Day in year (0-365) */
  1037.     if (! leap && t >= 59)              /* If after Feb non leap */
  1038.         t++;                      /* Fudge as if leap */
  1039.     mm = t / 31;                  /* Guess the month */
  1040.     if (t >= monthend[mm+1])              /* Check the guess */
  1041.         mm++;
  1042.     tm->tm_mday = t - monthend[mm] + 1;         /* Compute day in month */
  1043.     tm->tm_mon = mm;
  1044.     tm->tm_wday = d % 7;              /* Sunday = 0 */
  1045. }
  1046.  
  1047. static int tabfind (text, table)
  1048. char *text;
  1049. struct stable *table;
  1050. { int tp;
  1051.   char *tep, *txp;
  1052.   int find = -1, foundstar;
  1053.   wasabb = 0;
  1054.   for (tp = 0; table[tp].text; tp++)
  1055.   { tep = table[tp].text;
  1056.     txp = text;
  1057.     foundstar = 0;
  1058.     while (1)
  1059.     { if (*tep == '*')
  1060.       { foundstar = 1;
  1061.     tep++;
  1062.       }
  1063.       if (! *txp)                  /* If end of text */
  1064.       { if (! *tep)                  /* If also end of table entry */
  1065.       return (tp);                 /* then found */
  1066.     if (foundstar)
  1067.     { if (find >= 0)
  1068.         return (-2);              /* Ambiguous */
  1069.       find = tp;                  /* Remember partial match */
  1070.       wasabb = 1;                 /* Was abbrev. */
  1071.       break;
  1072.     }
  1073.       }
  1074.       if (*txp != *tep)
  1075.     break;                      /* No match */
  1076.       tep++;  txp++;
  1077.     }
  1078.   }
  1079.   return (find);
  1080. }
  1081.  
  1082. /* check: check that a date is valid. each of the constraint processing
  1083.  *   routines is called in turn and if any of them do anything then the
  1084.  *   date is invalid.
  1085.  */
  1086.  
  1087. static check (date)
  1088. int date;
  1089. { register int did;
  1090.   register struct dtm *d = &dtm[date];
  1091.   if (d->repn != RTM)
  1092.     return;
  1093.   did = month (d, d->tm.tm_mon, FUTURE);
  1094.   if (! did)
  1095.     did = mday (d, d->tm.tm_mday, FUTURE);
  1096.   if (! did && d->tm.tm_wday >= 0)
  1097.     did = weekday (d, d->tm.tm_wday, FUTURE);
  1098.   if (did)
  1099.     yyerror ();
  1100.   return;
  1101. }
  1102.  
  1103. /* constrain: fill in defaults info in date. con is a dtm containing the 
  1104.  *   constraints (or -1 indicating to use ptm). date is the dtm containing
  1105.  *   the date to be constrained. repeat is the loop count. */
  1106.  
  1107. static constrain (con, date, past, repeat)
  1108. int con, date;
  1109. int past, repeat;
  1110. { register int n;
  1111.   register int did;
  1112.   register struct tm *c;
  1113.   register struct dtm *d = &dtm[date];
  1114.   if (con >= 0)
  1115.     c = &(dtm[con].tm);
  1116.   else
  1117.     c = &ptm;
  1118.   if (c->tm_year != NOYEAR)
  1119.     yyerror ();
  1120.  
  1121.   for (n = 1000, did = 0; ; did = 0)
  1122.   { if (c->tm_mon >= 0)
  1123.     { did |= month (d, c->tm_mon, past);
  1124.     }
  1125.     if (c->tm_mday >= 0)
  1126.     { did |= mday (d, c->tm_mday, past);
  1127.     }
  1128.     if (c->tm_wday >= 0)
  1129.     { did |= weekday (d, c->tm_wday, past);
  1130.     }
  1131.     if (! did)
  1132.     { if (repeat-- <= 1)
  1133.         break;
  1134.       incr (date, past ? -1 : 1);
  1135.     }
  1136.     if (--n <= 0)
  1137.       yyerror ();
  1138.   }
  1139.  
  1140.   if (con >= 0)
  1141.     dtmused[con] = 0;
  1142.   else
  1143.   { ptm.tm_year = NOYEAR;
  1144.     ptm.tm_mon = -1;
  1145.     ptm.tm_mday = -1;
  1146.     ptm.tm_wday = -1;
  1147.     ptm.tm_yday = -1;
  1148.   }
  1149. }
  1150.  
  1151. static time_def (tm, currtm)
  1152. struct tm *tm, *currtm;
  1153. { if (shour >= 0)            /* Handle simple hour specification */
  1154.   { if (tmkey == -1)            /* and combine it with time key. */
  1155.       tmkey = 8;            /* Default is 8:00 - 19:59 */
  1156.     if (shour == 12)
  1157.       shour = 0;
  1158.     if (shour < tmkey)
  1159.       shour += 12;
  1160.     if (shour < tmkey)
  1161.       shour += 12;
  1162.     tm->tm_hour = shour;
  1163.   }
  1164.   if (tm->tm_hour >= 0)
  1165.   { /* If time specified and fields left out, assume zero. */
  1166.     if (tm->tm_min < 0)
  1167.       tm->tm_min = 0;
  1168.     if (tm->tm_sec < 0)
  1169.       tm->tm_sec = 0;
  1170.   }
  1171. }
  1172.  
  1173. /* date constraint processing routines.
  1174.  * 
  1175.  * These routines allow determination of the first date after/before
  1176.  * (but possibly equal to the existing date) which satisfies the given
  1177.  * constraint(s).
  1178.  */
  1179.  
  1180. /* The constraints are implemented by calling the appropriate routine(s)
  1181.  * which check whether the particular constraint is satisfied, and if it
  1182.  * is not, advances the date until the constraint is satisfied.
  1183.  * 
  1184.  * The date is stored in the dtm structure.
  1185.  */
  1186.  
  1187. /* weekday: constrains the day of the week. */
  1188.  
  1189. static int weekday (dtm, wkday, past)
  1190. struct dtm *dtm;
  1191. int wkday;                      /* 0 = Sunday */
  1192. int past;                      /* true = past */
  1193. { int n;
  1194.   setrep (dtm, RDAYS);
  1195.   n = wkday - dtm->days % 7;          /* adjustment */
  1196.   if (past)
  1197.   { if (n > 0)
  1198.       n -= 7;
  1199.   }
  1200.   else {
  1201.     if (n < 0 || (nottoday && n == 0))
  1202.       n += 7;
  1203.     nottoday = 0;
  1204.   }
  1205.   dtm->days += n;
  1206.   return (n != 0);
  1207. }
  1208.  
  1209. /* month: constrains the month to the specified value. */
  1210.  
  1211. static int month (dtm, mon, past)
  1212. struct dtm *dtm;
  1213. int mon;
  1214. int past;
  1215. { setrep (dtm, RTM);
  1216.   if (mon < 0 || mon > 11)
  1217.     yyerror ();
  1218.   if (dtm->tm.tm_mon != mon)
  1219.   { if (past)
  1220.     { if (dtm->tm.tm_mon < mon)          /* If earlier month */
  1221.     dtm->tm.tm_year--;              /* Back up a year */
  1222.       dtm->tm.tm_mday = monthend[mon+1] - monthend[mon];
  1223.       if (mon == 1 && yearsize (dtm->tm.tm_year+1900) == 365)  /* Feb */
  1224.     dtm->tm.tm_mday--;
  1225.     }
  1226.     else
  1227.     { if (dtm->tm.tm_mon > mon)          /* If later month */
  1228.     dtm->tm.tm_year++;              /* Advance a year */
  1229.       dtm->tm.tm_mday = 1;
  1230.     }
  1231.     dtm->tm.tm_mon = mon;
  1232.     dtm->tm.tm_wday = dtm->tm.tm_yday = -1;
  1233.     return (1);
  1234.   }
  1235.   return (0);
  1236. }
  1237.  
  1238. /* mday: constrains the month day to the specified value. Also
  1239.  *   checks the validity of the specified value and, if it is invalid,
  1240.  *   adjusts the month to compensate. */
  1241.  
  1242. static int mday (dtm, day, past)
  1243. struct dtm *dtm;
  1244. int day;
  1245. int past;
  1246. { register int maxday;
  1247.   register int status = 0;
  1248.   setrep (dtm, RTM);
  1249.   if (dtm->tm.tm_mday != day)
  1250.   { if (past)
  1251.     { if (dtm->tm.tm_mday < day)           /* Earlier day */
  1252.         if (dtm->tm.tm_mon-- == 0)         /* Back up a month */
  1253.     { dtm->tm.tm_mon = 11;
  1254.       dtm->tm.tm_year--;
  1255.     }
  1256.     }
  1257.     else
  1258.     { if (dtm->tm.tm_mday > day)           /* Later day */
  1259.     if (dtm->tm.tm_mon++ == 11)           /* Advance month */
  1260.     { dtm->tm.tm_mon = 0;
  1261.       dtm->tm.tm_year++;
  1262.     }
  1263.     }
  1264.     dtm->tm.tm_mday = day;
  1265.     dtm->tm.tm_wday = dtm->tm.tm_yday = -1;
  1266.     status = 1;
  1267.   }
  1268.   if (day >= 28)
  1269.   { maxday = monthend[dtm->tm.tm_mon+1] - monthend[dtm->tm.tm_mon];
  1270.     if (dtm->tm.tm_mon == 1 && yearsize (dtm->tm.tm_year + 1900) == 365)
  1271.       maxday--;
  1272.     if (day > maxday)
  1273.     { if (past)
  1274.         dtm->tm.tm_mday = maxday;
  1275.       else
  1276.       { dtm->tm.tm_mday = 1;
  1277.     if (dtm->tm.tm_mon++ == 11)
  1278.     { dtm->tm.tm_mon = 0;
  1279.       dtm->tm.tm_year++;
  1280.     }
  1281.       }
  1282.       dtm->tm.tm_wday = dtm->tm.tm_yday = -1;
  1283.       return (1);
  1284.     }
  1285.   }
  1286.   return (status);
  1287. }
  1288.  
  1289. /* setrep: sets the representation of the date in a dtm structure. */
  1290.  
  1291. static setrep (dtm, rep)
  1292. struct dtm *dtm;
  1293. int rep;
  1294. { if (dtm->repn == rep)
  1295.     return;
  1296.   if (dtm->repn == RDAYS)
  1297.   { if (rep == RTM)
  1298.       days_date (dtm->days, &(dtm->tm));
  1299.   }
  1300.   else if (dtm->repn == RTM)
  1301.   { if (rep == RDAYS)
  1302.       dtm->days = date_days (&(dtm->tm));
  1303.   }
  1304.   dtm->repn = rep;
  1305.   return;
  1306. }
  1307.  
  1308. /* yearsize: returns nuber of days in year. */
  1309.  
  1310. static yearsize (year)
  1311. int year;
  1312. { return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) ? 366 : 365);
  1313. }
  1314.  
  1315. /* new_dtm: returns the index of a new dtm structure. If current is CURRTM,
  1316.  *   the structure will contain a copy of the current date-time, else it
  1317.  *   will contain a copy of ptm, and ptm will be reset to all -1.
  1318.  */
  1319.  
  1320. static int new_dtm (current)
  1321. int current;
  1322. { register int i;
  1323.   for (i = 0; i < NDTMS; i++)
  1324.     if (! dtmused[i])
  1325.     { dtmused[i] = 1;
  1326.       if (current == CURRTM || current == CURRDATE)
  1327.       { dtm[i].tm = *currtm;
  1328.         if (current == CURRDATE)
  1329.     { dtm[i].tm.tm_hour = -1;
  1330.       dtm[i].tm.tm_min = -1;
  1331.       dtm[i].tm.tm_sec = -1;
  1332.     }
  1333.     dtm[i].repn = RTM;
  1334.     dtm[i].count = 0;
  1335.       }
  1336.       else
  1337.       { dtm[i].tm = ptm;
  1338.     dtm[i].count = pcount;
  1339.     dtm[i].repn = RTM;
  1340.     ptm.tm_year = NOYEAR;
  1341.     ptm.tm_mon = -1;
  1342.     ptm.tm_mday = -1;
  1343.     ptm.tm_wday = -1;
  1344.     ptm.tm_yday = -1;
  1345.     pcount = 0;
  1346.       }
  1347.       return (i);
  1348.     }
  1349.   yyerror ();
  1350. }
  1351.  
  1352. /* incr: increment date in dtm structure. */
  1353.  
  1354. static incr (ndtm, days)
  1355. int ndtm;
  1356. int days;
  1357. { setrep (&dtm[ndtm], RDAYS);
  1358.   dtm[ndtm].days += days;
  1359. }
  1360.  
  1361. static incryear (ndtm, years)
  1362. int ndtm;
  1363. int years;
  1364. { setrep (&dtm[ndtm], RTM);
  1365.   dtm[ndtm].tm.tm_year += years;
  1366.   dtm[ndtm].tm.tm_wday = -1;        /* Unknown */
  1367.   dtm[ndtm].tm.tm_yday = -1;        /* Unknown */
  1368. }
  1369.  
  1370. /* incrmonth: increment date in dtm structure by a number of months.*/
  1371.  
  1372. static incrmonth (ndtm, months)
  1373. int ndtm;
  1374. int months;
  1375. { int inc;
  1376.   inc = months > 0 ? 1 : -1;
  1377.   setrep (&dtm[ndtm], RTM);        /* Use tm structure repn */
  1378.   for ( ; months != 0; months -= inc)
  1379.   { dtm[ndtm].tm.tm_mon += inc;
  1380.     if (dtm[ndtm].tm.tm_mon < 0)
  1381.     { dtm[ndtm].tm.tm_mon = 11;
  1382.       dtm[ndtm].tm.tm_year--;
  1383.     }
  1384.     else if (dtm[ndtm].tm.tm_mon > 11)
  1385.     { dtm[ndtm].tm.tm_mon = 0;
  1386.       dtm[ndtm].tm.tm_year++;
  1387.     }
  1388.   }
  1389.   dtm[ndtm].tm.tm_wday = -1;         /* Day of week is unknown */
  1390.   dtm[ndtm].tm.tm_yday = -1;        /* Day of year is unknown */
  1391. }
  1392.